#include <time.h>
#include <fuse.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include "util.h"
#include "dirops.h"
#include "cache.h"

#define ROOT_KEY "1234567890123456789012345678901"
#define ROOT_IV "1234567890123456789012345678901"


/* Following code is kind of unused, should be cleaned up */
#if 0

/* Shifts characters starting from (and including) PTR (array index,
   starts from 0) by NUM chars towards left */
void shift_left (char *s, int ptr, int num) {
  assert (ptr >= num);
  int len = strlen (s);
  memmove (s+ptr-num, s+ptr, len-ptr+1); /* +1 for \0 */
}

int check_consec_slashes (const char *s, int ptr) {
  if (ptr == 0) 
    return 0;
  if (s[ptr] == '/' && s[ptr-1] == '/')
    return 1;
  else 
    return 0;
}

int check_single_dot (char *s, int ptr) {
  if (ptr < 2)
    return 0;
  if (s[ptr] == '/' && s[ptr-1] == '.' && s[ptr-2] == '/')
    return 1;
  else 
    return 0;
}

int check_double_dot (char *s, int ptr) {
  if (ptr < 3)
    return 0;
  if (s[ptr] == '/' && s[ptr-1] == '.' 
        && s[ptr-2] == '.' && s[ptr-3] == '/') {
    /* Let's find the location of next / */
    int ret = 4;
    while (ptr-ret >= 0 && s[ptr-ret] != '/') {
      ret++;
    }
    if (ptr-ret < 0)
      return ptr;
    return ret; 
  } else
    return 0;
}


void sanitize_path (const char *path, char *new_path) {

  strcpy (new_path, path);
  int curr=0;
  while (new_path[curr] != '\0') {
    if (check_consec_slashes (new_path, curr) != 0) {
      shift_left (new_path, curr, 1);
      curr--;
      continue;
    }
    if (check_single_dot (new_path, curr) != 0) {
      shift_left (new_path, curr, 2);
      curr = curr-2;
      continue;
    }
    int tmp = check_double_dot (new_path, curr);  
    /* tmp captures the amount to be shifted in case there is a need) */
    if (tmp != 0) {
      shift_left (new_path, curr, tmp); 
      curr = curr-tmp;
      continue;
    }
    curr++;
  }
}

#endif

void get_parent (const char *path, char *parent) {
  /* Assume parent is at least as big as path, and 
     the path is well sanitized */
  int len = strlen (path);
  strcpy (parent, path);
  int i = len-2;    /* Ignore last character */
  while (i>=0 && parent[i] != '/') {
    i--;
  }
  if (i<=0) i=1;
  parent[i] = '\0';
}

/* At least length MAX_FNAME_LEN should be allocated for name */
void get_filename (const char *path, char *name) {

  int r = strlen (path);
  if (path[r-1] == '/') 
    r--;
  int l = r;
  while (l>=0 && path[l] != '/')
    l--;
  l++; /* Because l-1 points to / */
  assert (r-l <= MAX_FNAME_LEN-1);
  strncpy (name, path+l, r-l);
  name[r-l] = '\0';
}

/* 
  Takes the absolute path, and finds the corresponding object-id, if 
  one exists.
  Returns 0 in case of success, negative otherwise
*/

int get_object_id (const char *path, object_id *id) {

  assert (path[0] == '/');

  memset (id, 0, sizeof (object_id));
  /* Base case (i.e. root). This should definitely exist */
  if (strcmp (path, "/") == 0) {

    id->type = DIR_OBJ;
    /* Special name for root object, i.e. all 0's */
    memset (id->id, '0', IDLEN);
    id->ver = 0; id->dev = 0;
    strcpy ((char *)(id->key).key, ROOT_KEY);
    strcpy ((char *)(id->key).iv, ROOT_IV);
    return 0;

  } else {

    int ret = 0;

    /* Fetch the parent object first */ 
    char *parent_path = malloc (strlen (path)+1);
    get_parent (path, parent_path);
    object *parent_object = malloc (sizeof (object));
    void *buf = malloc (MAX_DATA_OBJ);

    ret = get_object_from_path (parent_path, parent_object,
                            MAX_DATA_OBJ, buf);    

    if (ret == 0) { /* Parent exists */
      if (parent_object->type == DIR_OBJ) { /* Parent is a directory */
        /* Get the name that we need to lookup for */
        char name[MAX_FNAME_LEN];
        get_filename (path, name);

        /* Lookup dentry with this name */
        dentry *d = malloc (sizeof (dentry));
        ret = dentry_lookup (parent_object, name, d);
        if (ret == 0) {
          /* Such name exists in the parent */
          memcpy (id, &(d->obj_id), sizeof (object_id));
        } 

        /* ret is set to -ENOENT in case dentry_lookup fails */
        free (d);
      } else {  /* Parent exists, but is not a directory */
        ret = -ENOENT;
      }
    } 

    free (buf);
    free (parent_object);
    free (parent_path);
    
    return ret;
  }
}

/* 
  Returns 0 on success, negative otherwise
*/

int get_object_from_path (const char *path, object *obj, 
                        int max_length, void *buf) {

  int ret = 0;
  /* First get the object_id of the corresponding path */

  object_id *id = malloc (sizeof (object_id));
  if (id == NULL) 
    return -ENOMEM;

  ret = get_object_id (path, id);  
  if (ret == 0) {
    ret = get_object_c (id, obj, max_length, buf);
    /* ret would indicate the length of the object */
    if (ret >= 0)
      ret = 0;
    else
      ret = -ENOENT;
  }

  free (id);

  return ret;
}

int save_object_with_path (const char *path, object *obj) {

  object_id *id = malloc (sizeof (object_id));
  int ret = get_object_id (path, id);
  if (ret == 0) {
    put_object_c (id, obj);
  }
  free (id);
  return ret;
}
